#include <stdio.h>
#include <stdarg.h>
#include <stdint.h>
#include <ctype.h>
#include <stddef.h>
#include <string.h>
#include <stdlib.h>
#include <yonex/limits.h>
#include <assert.h>

#define MAX(a,b)   ((a)>(b)?(a):(b))
#define MIN(a,b)   ((a)<(b)?(a):(b))

#define PLUS		1
#define LEFT		2
#define SPACE		4
#define SPECIAL		8
#define ZEROPAD		16
#define LOWER		32
#define SIGN		64
#define NEGATIVE	128

static const char xdigits[16] = {
	"0123456789ABCDEF"
};

// 将变量转成16进制字符串
static char *fmt_x(uintmax_t x, char *s, int lower)
{
	for (; x; x>>=4) *--s = xdigits[(x&15)]|lower;
	return s;
}

// 将变量转成8进制字符串
static char *fmt_o(uintmax_t x, char *s)
{
	for (; x; x>>=3) *--s = '0' + (x&7);
	return s;
}

// 将变量转成10进制字符串
static char *fmt_u(uintmax_t x, char *s)
{
	unsigned long y;
	for (; x>ULONG_MAX; x/=10) *--s = '0' + x%10;
	for (y=x;        y; y/=10) *--s = '0' + y%10;
	return s;
}

// 从给定的字符串中提取一个整数值，并更新字符串指针
static int getint(const char **s) {
	int i;
	for (i=0; isdigit(**s); (*s)++) {
		if (i > INT_MAX/10U || **s-'0' > INT_MAX-10*i) i = -1;
		else i = 10*i + (**s-'0');
	}
	return i;
}

static char buf[32];

static int printf_core(char *str, const char *fmt, va_list ap)
{
	char *s = str;
	int len;
	const char *src;
	uintmax_t num;
	// %(flags)(width)(.prec)(length)[type]
	int flags = 0;	/* flags to number() */
    int width;		/* width of output field */
    int prec;		/* min. # of digits for integers; max number of chars for from string */
    int qualifier;	/* 'h', 'l', or 'L' for integer fields */
					/* 'z' support added 23/7/1999 S.H.    */
					/* 'z' changed to 'Z' --davidm 1/25/99 */
	for (; *fmt; fmt++) {
		if (*fmt != '%') {
			*s++ = *fmt;
		}
		else {
			// flags
			flags = 0;
			repeat:
			fmt++;
			switch (*fmt) {
				case '+': flags |= PLUS;	goto repeat;
				case '-': flags |= LEFT;	goto repeat;
				case ' ': flags |= SPACE;	goto repeat;
				case '#': flags |= SPECIAL;	goto repeat;
				case '0': flags |= ZEROPAD;	goto repeat;
			}

			// width
			if (*fmt == '*') {
				width = va_arg(ap, int);
				fmt++;
			}
			else {
				width = getint(&fmt);
			}

			// precision
			prec = -1;
			if (*fmt == '.') {
				fmt++;
				if (*fmt == '*') {
					prec = va_arg(ap, int);
					fmt++;
				}
				else prec = getint(&fmt);
			}

			// length qualifier
			qualifier = 0;
			if (*fmt == *(fmt+1)) {
				if (*fmt == 'l')
					qualifier = 'q';
				else if (*fmt == 'h')
					qualifier = 'b';
				fmt+=2;
			}
			else if (*fmt == 'h' || *fmt == 'l' || *fmt == 'z') {
            	qualifier = *fmt;
            	++fmt;
			}

			// type
			switch (*fmt) {
				case 'c':
					len = 1;
					if (!(flags&LEFT))
						for(; (width-len)>0; width--) *s++ = ' ';
					*s++ = (char)va_arg(ap, long);
					for(; (width-len)>0; width--) *s++ = ' ';
					continue;
				case 's':
					src = va_arg(ap, char *);
					len = strnlen(src, prec);
					if (!(flags&LEFT))
						for(; (width-len)>0; width--) *s++ = ' ';
					strncpy(s, src, len);
					s += len;
					for(; (width-len)>0; width--) *s++ = ' ';
					continue;
				case 'p':
					if (prec == -1)
						prec = 8;	// 默认%p的数字宽度最小为8
					src = fmt_x(va_arg(ap, uintmax_t), buf+31, LOWER);
					len = strlen(src);
					if (!(flags&ZEROPAD))
						if (!(flags&LEFT))
							for(; (width-MAX(len, prec)-2)>0; width--) *s++ = ' ';
					*s++ = '0'; *s++ = 'x';
					if (flags&ZEROPAD)
						for(; (width-MAX(len, prec)-2)>0; width--) *s++ = '0';
					for(int i = prec; (i-len)>0; i--) *s++ = '0';
					strncpy(s, src, len);
					s += len;
					for(; (width-MAX(len, prec)-2)>0; width--) *s++ = ' ';
					continue;
				case 'd':
				case 'i':
					flags |= SIGN;
					break;
				case 'x':
					flags |= LOWER;
				case 'X':
				case 'u':
				case 'o':
					break;
				default : assert(0);
			}
			// 处理精度
			if (prec == -1)	prec = 1;

			// 处理长度修饰
			if (qualifier == 'l' || qualifier == 'z' || qualifier == 'q')
				num = va_arg(ap, uintmax_t);
			else if (flags&SIGN) {
				if (qualifier == 'h')
					num = (uintmax_t)(short)va_arg(ap, uintmax_t);
				else if (qualifier == 'b')
					num = (uintmax_t)(char)va_arg(ap, uintmax_t);
				else
					num = (uintmax_t)(int)va_arg(ap, uintmax_t);
			}
			else {
				if (qualifier == 'h')
					num = (uintmax_t)(unsigned short)va_arg(ap, uintmax_t);
				else if (qualifier == 'b')
					num = (uintmax_t)(unsigned char)va_arg(ap, uintmax_t);
				else
					num = (uintmax_t)(unsigned)va_arg(ap, uintmax_t);
			}
			if (flags&SIGN && (num >> 63))
				flags |= NEGATIVE;

			switch (*fmt) {
				case 'd':
				case 'i':
					if (flags&NEGATIVE) {
						src = fmt_u(labs(num), buf+31);
						len = strlen(src);
						if (!(flags&ZEROPAD))
							if (!(flags&LEFT))
								for(; (width-MAX(len, prec)-1)>0; width--) *s++ = ' ';
						*s++ = '-';
						if ((flags&ZEROPAD))
							for(; (width-MAX(len, prec)-1)>0; width--) *s++ = '0';
					}
					else {
						src = fmt_u(num, buf+31);
						len = strlen(src);
						if (flags&PLUS) {
							if (!(flags&ZEROPAD))
								if (!(flags&LEFT))
									for(; (width-MAX(len, prec)-1)>0; width--) *s++ = ' ';
							*s++ = '+';
							if ((flags&ZEROPAD))
								for(; (width-MAX(len, prec)-1)>0; width--) *s++ = '0';
						}
						else if (flags&SPACE) {
							if (!(flags&ZEROPAD))
								if (!(flags&LEFT))
									for(; (width-MAX(len, prec)-1)>0; width--) *s++ = ' ';
							*s++ = ' ';
							if ((flags&ZEROPAD))
								for(; (width-MAX(len, prec)-1)>0; width--) *s++ = '0';
						}
						else if ((flags&ZEROPAD))
							for(; (width-MAX(len, prec))>0; width--) *s++ = '0';
						else if (!(flags&LEFT))
							for(; (width-MAX(len, prec))>0; width--) *s++ = ' ';
					}
					for(int i = prec; (i-len)>0; i--) *s++ = '0';
					strncpy(s, src, len);
					s += len;
					if (flags&(PLUS|SPACE)) for(; (width-MAX(len, prec)-1)>0; width--) *s++ = ' ';
					else for(; (width-MAX(len, prec))>0; width--) *s++ = ' ';
					continue;
				case 'x':
				case 'X':
					src = fmt_x(num, buf+31, flags&LOWER);
					len = strlen(src);
					if (flags&SPECIAL) {
						if (!(flags&ZEROPAD))
							if (!(flags&LEFT))
								for(; (width-MAX(len, prec)-2)>0; width--) *s++ = ' ';
						*s++ = '0'; *s++ = 'x';
						if ((flags&ZEROPAD))
							for(; (width-MAX(len, prec)-2)>0; width--) *s++ = '0';
					}
					else {
						if (flags&ZEROPAD)
							for(; (width-MAX(len, prec))>0; width--) *s++ = '0';
						else if (!(flags&LEFT))
							for(; (width-MAX(len, prec))>0; width--) *s++ = ' ';
					}
					for(int i = prec; (i-len)>0; i--) *s++ = '0';
					strncpy(s, src, len);
					s += len;
					if (flags&SPECIAL) for(; (width-MAX(len, prec)-2)>0; width--) *s++ = ' ';
					else for(; (width-MAX(len, prec))>0; width--) *s++ = ' ';
					continue;
				case 'u':
					src = fmt_u(num, buf+31);
					len = strlen(src);
					if ((flags&ZEROPAD))
						for(; (width-MAX(len, prec))>0; width--) *s++ = '0';
					else if (!(flags&LEFT))
						for(; (width-MAX(len, prec))>0; width--) *s++ = ' ';
					for(int i = prec; (i-len)>0; i--) *s++ = '0';
					strncpy(s, src, len);
					s += len;
					for(; (width-MAX(len, prec))>0; width--) *s++ = ' ';
					continue;
				case 'o':
					src = fmt_o(num, buf+31);
					len = strlen(src);
					if (flags&SPECIAL) {
						if (!(flags&ZEROPAD))
							if (!(flags&LEFT))
								for(; (width-MAX(len, prec)-1)>0; width--) *s++ = ' ';
						*s++ = '0';
						if (flags&ZEROPAD)
							for(; (width-MAX(len, prec)-1)>0; width--) *s++ = '0';
					}
					else {
						if (flags&ZEROPAD)
							for(; (width-MAX(len, prec))>0; width--) *s++ = '0';
						else if (!(flags&LEFT))
							for(; (width-MAX(len, prec))>0; width--) *s++ = ' ';
					}
					for(int i = prec; (i-len)>0; i--) *s++ = '0';
					strncpy(s, src, len);
					s += len;
					if (flags&SPECIAL) for(; (width-MAX(len, prec)-1)>0; width--) *s++ = ' ';
					else for(; (width-MAX(len, prec))>0; width--) *s++ = ' ';
					continue;
			}
		}
	}
	*s++ = '\0';
	assert((s-str)<1024);
	return s - str;
}

int vsprintf(char *str, const char *fmt, va_list ap)
{
	return printf_core(str, fmt, ap);
}
